//
// number.c
// simple math
//
// (c) 2008 why the lucky stiff, the freelance professor
//
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <math.h>
#include "potion.h"
#include "internal.h"

// 取得 double ： 整数则转换， PNDecimal 直接提取。
#define PN_DBL(num) (PN_IS_NUM(num) ? (double)PN_INT(num) : ((struct PNDecimal *)num)->value)

// 用 double v 构建 PNDecimal ， .vt=PN_TNUMBER 。
PN potion_real(Potion *P, double v) {
  vPN(Decimal) d = PN_ALLOC(PN_TNUMBER, struct PNDecimal);
  d->value = v;
  return (PN)d;
}

// len 长度的字符串 str 转为 PNDecimal 。
PN potion_decimal(Potion *P, char *str, int len) {
  char *ptr = str + len;
  return potion_real(P, strtod(str, &ptr));
}

// self^sup ，双整数则为整数，否则为 PNDecimal 。
PN_MSG_G(potion_pow, PN sup) {
  double x = PN_DBL(self), y = PN_DBL(sup);
  double z = pow(x, y);
  if (PN_IS_NUM(self) && PN_IS_NUM(sup))
    return PN_NUM((int)z);
  return potion_real(P, z);
}

// 求平方根，结果是 PNDecimal 。
PN_MSG(potion_sqrt) {
  return potion_real(P, sqrt(PN_DBL(self)));
}

// 二元数学元算 self int_math num ：整数 + 整数，或转为 double 算得 PNDecimal 。
// TODO hsq 没有直接调用，是通过发消息调用的，而发消息前已经检测是否双整数参数，所以这里可不用检测；
//    以及 potion_rem potion_bitl potion_bitr potion_pipe potion_caret potion_amp；
//    但是测试中有对双整数发这些消息，所以删除会造成测试失败。
#define PN_NUM_MATH(int_math) \
  if (PN_IS_NUM(self) && PN_IS_NUM(num)) \
    return PN_NUM(PN_INT(self) int_math PN_INT(num)); \
  return potion_real(P, PN_DBL(self) int_math PN_DBL(num));

// 加法，整数 + 整数，或转为 double 算得 PNDecimal 。
PN_MSG(potion_add, PN num) {
  PN_NUM_MATH(+)
}

// 减法，整数 - 整数，或转为 double 算得 PNDecimal 。
PN_MSG(potion_sub, PN num) {
  PN_NUM_MATH(-)
}

// 乘法，整数 ✖️ 整数，或转为 double 算得 PNDecimal 。
PN_MSG(potion_mult, PN num) {
  PN_NUM_MATH(*)
}

// 除法，整数 ➗ 整数，或转为 double 算得 PNDecimal 。
// TODO hsq 除 0 报错 Floating point exception: 8 ，如何实现的？用无穷大？
PN_MSG(potion_div, PN num) {
  PN_NUM_MATH(/)
}

// 求余，整数 % 整数，或转为 double 算得整数商再算余数并构建 PNDecimal 。
PN_MSG(potion_rem, PN num) {
  if (PN_IS_NUM(self) && PN_IS_NUM(num))
    return PN_NUM(PN_INT(self) % PN_INT(num));
  double x = PN_DBL(self), y = PN_DBL(num);
  int z = (int)(x / y);
  return potion_real(P, x - (y * (double)z));
}

// 整数按位取反，否则为 0.0 。
PN_MSG(potion_bitn) {
  if (PN_IS_NUM(self))
    return PN_NUM(~PN_INT(self));
  return (PN)potion_real(P, 0.0);
}

// 左移位： 整数 << 整数，否则为 0.0 。
PN_MSG(potion_bitl, PN num) {
  if (PN_IS_NUM(self) && PN_IS_NUM(num))
    return PN_NUM(PN_INT(self) << PN_INT(num));
  return (PN)potion_real(P, 0.0);
}

// 左移位： 整数 >> 整数，否则为 0.0 。
PN_MSG(potion_bitr, PN num) {
  if (PN_IS_NUM(self) && PN_IS_NUM(num))
    return PN_NUM(PN_INT(self) >> PN_INT(num));
  return (PN)potion_real(P, 0.0);
}

// 左移位： 整数 | 整数，否则为 0.0 。
PN_MSG(potion_pipe, PN num) {
  if (PN_IS_NUM(self) && PN_IS_NUM(num))
    return PN_NUM(PN_INT(self) | PN_INT(num));
  return (PN)potion_real(P, 0.0);
}

// 左移位： 整数 ^ 整数，否则为 0.0 。
PN_MSG(potion_caret, PN num) {
  if (PN_IS_NUM(self) && PN_IS_NUM(num))
    return PN_NUM(PN_INT(self) ^ PN_INT(num));
  return (PN)potion_real(P, 0.0);
}

// 左移位： 整数 & 整数，否则为 0.0 。
PN_MSG(potion_amp, PN num) {
  if (PN_IS_NUM(self) && PN_IS_NUM(num))
    return PN_NUM(PN_INT(self) & PN_INT(num));
  return (PN)potion_real(P, 0.0);
}

// 返回自身
PN_MSG(potion_num_number) {
  return self;
}

// 从整数 self 到整数 end （包含），每隔整数 step 执行一次闭包 block ；
//    self 也是 block 的 self ， block 具有唯一参数：步点 i 。
PN_MSG(potion_num_step, PN end, PN step, PN block) {
  int i, j = PN_INT(end), k = PN_INT(step);
  PN r;
  for (i = PN_INT(self); i <= j; i += k) {
    r = PN_CLOSURE(block)->method(P, block, self, PN_NUM(i));
  }
  return r;
}

// 字符串化，对于浮点数则消除末尾的 0 。
PN_MSG_G(potion_num_string) {
  char ints[40];
  if (PN_IS_NUM(self)) {
    sprintf(ints, "%ld", PN_INT(self));
    return PN_STRING(ints);
  } else {
    int len = sprintf(ints, "%.16f", ((struct PNDecimal *)self)->value);
    while (len > 0 && ints[len - 1] == '0') len--;
    if (ints[len - 1] == '.') len++;
    ints[len] = '\0';
    return potion_str2(P, ints, len);
  }
}

// 执行整数 self 次闭包 block，返回执行次数；
//    self 也是 block 的 self ， block 具有唯一参数：次数 i 。
PN_MSG(potion_num_times, PN block) {
  int i, j = PN_INT(self);
  for (i = 0; i < j; i++)
    PN_CLOSURE(block)->method(P, block, self, PN_NUM(i));
  return PN_NUM(i);
}

// 从整数 self 到整数 end （包含），每个执行一次闭包 block ，返回执行次数；
//    self 也是 block 的 self ， block 具有唯一参数：步点 i 。
PN_MSG(potion_num_to, PN end, PN block) {
  int i, s = 1, j = PN_INT(self), k = PN_INT(end);
  if (k < j) s = -1;
  for (i = j; i != k + s; i += s)
    PN_CLOSURE(block)->method(P, block, self, PN_NUM(i));
  return PN_NUM(abs(i - j));
}

// 注册虚表 Number 的消息（运算符和方法）。
void potion_num_init(Potion *P) {
  PN num_vt = PN_VTABLE(PN_TNUMBER);
  PN_METHOD(num_vt, "+",      potion_add,        "value=N");
  PN_METHOD(num_vt, "-",      potion_sub,        "value=N");
  PN_METHOD(num_vt, "*",      potion_mult,       "value=N");
  PN_METHOD(num_vt, "✖️",      potion_mult,       "value=N");
  PN_METHOD(num_vt, "/",      potion_div,        "value=N");
  PN_METHOD(num_vt, "➗",     potion_div,        "value=N");
  PN_METHOD(num_vt, "%",      potion_rem,        "value=N");
  PN_METHOD(num_vt, "~",      potion_bitn,       0);
  PN_METHOD(num_vt, "<<",     potion_bitl,       "value=N");
  PN_METHOD(num_vt, ">>",     potion_bitr,       "value=N");
  PN_METHOD(num_vt, "|",      potion_pipe,       "value=N");
  PN_METHOD(num_vt, "^",      potion_caret,      "value=N");
  PN_METHOD(num_vt, "&",      potion_amp,        "value=N");
  PN_METHOD(num_vt, "**",     potion_pow,        "value=N");
  PN_METHOD(num_vt, "number", potion_num_number, 0);
  PN_METHOD(num_vt, "sqrt",   potion_sqrt,       0);
  PN_METHOD(num_vt, "step",   potion_num_step,   "end=N,step=N,block=&");
  PN_METHOD(num_vt, "string", potion_num_string, 0);
  PN_METHOD(num_vt, "times",  potion_num_times,  "block=&");
  PN_METHOD(num_vt, "to",     potion_num_to,     "end=N,block=&");
}
